{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 第9章 面向对象"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-1 类的定义\n",
    "\n",
    "+ 1、类关键字：class\n",
    "+ 2、类的名字：建议第一个字母大写，不用下划线而直接用首字母大写连接\n",
    "+ 3、要使用类，必须使类实例化；\n",
    "+ 4、类的基本作用：封装代码；\n",
    "+ 5、类只负责定义，不能在类的内部运行或调用类。\n",
    "+ 6、类方法一定要在括号中加入self，且在函数内部使用类中所定义的变量时，要在前面加入self. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student():\n",
    "    name  = '梁山广'\n",
    "    age = 27\n",
    "    \n",
    "    def print_info(self):\n",
    "        print(\"name = \" + self.name)\n",
    "        print(\"age = \" + str(self.age))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "student = Student()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name = 梁山广\n",
      "age = 27\n"
     ]
    }
   ],
   "source": [
    "student.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-2 方法与函数区别\n",
    "\n",
    "+ 1.没有绝对区别；\n",
    "+ 2.方法：设计层面（java、c#），是面向对象的；函数：程序运行、过程式的一种称谓（c、c++）\n",
    "+ 3.类下面定义的为方法，如果只是在模块里面的为函数\n",
    "\n",
    "变量与数据成员区别：\n",
    "\n",
    "+ 1.定义在模块里面称为变量\n",
    "+ 2.定义在类中称为数据成员，在于体现类的封装性\n",
    "\n",
    "没有必要纠结于此"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-4 构造函数\n",
    "\n",
    "作用: 实例化过程自动调用构造函数,可以在构造函数里初始化对象的特征，构造函数可以让类生成不同的对象,实例化传入不同的参数\n",
    "\n",
    "python中构造函数的格式：\n",
    "```python\n",
    "class Student():\n",
    "    name  = '梁山广'\n",
    "    age = 27\n",
    "    \n",
    "    def __init__(self,name,age):\n",
    "    self.name=name\n",
    "    self.age=age\n",
    "    \n",
    "    def print_info(self):\n",
    "        print(\"name = \" + self.name)\n",
    "        print(\"age = \" + str(self.age))\n",
    "        \n",
    "student1 = Student('石敢当',18)  #构造函数在实例化的时候自动调用\n",
    "\n",
    "```\n",
    "注：python中的构造函数可以显示调用，但很少这样做。如：\n",
    "```python\n",
    "\n",
    "student1.__init__()\n",
    "```\n",
    "此时用这种显示方式调用构造函数与调用普通函数无异，并且这样调用也只返回None,不能强制返回其他类型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name = 石敢当\n",
      "age = 18\n"
     ]
    }
   ],
   "source": [
    "class Student():\n",
    "    name  = '梁山广'\n",
    "    age = 27\n",
    "\n",
    "    def __init__(self, name, age):\n",
    "        self.name=name\n",
    "        self.age=age\n",
    "\n",
    "    def print_info(self):\n",
    "        print(\"name = \" + self.name)\n",
    "        print(\"age = \" + str(self.age))\n",
    "\n",
    "student = Student('石敢当',18)  #构造函数在实例化的时候自动调用\n",
    "student.print_info()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-6 类变量和实例变量\n",
    "\n",
    "+ 类变量就是和类相关联的变量\n",
    "+ 实例变量是和对象相关联的变量,在构造函数里通过self.变量名  来保存实例变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student():\n",
    "    # 此处定义的是类变量\n",
    "    name  = '梁山广'\n",
    "    age = 27\n",
    "    def __init__(self, name, age):\n",
    "        # 此处定义的是实例变量\n",
    "        self.name = name\n",
    "        self.age = age"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-7 类与对象的变量查找顺序\n",
    "\n",
    "> 变量采集链条：自内而外，自下而上，从子到父\n",
    "\n",
    "+ 1、对象中内置`__dict__`：以字典的方式返回对象中所有变量。调用方式:`对象名.__dict__`  \n",
    "  > 也可以访问类的__dict__，返回当前类的所有相关变量(类变量)。调用方式:类名.__dict__\n",
    "\n",
    "+ 2、变量寻找机制：python会先在实例中查找 若实例中没有找到(`__dict__`中没有该变量)python不会返回 会再去类中查找 类中找不到 会再到父类中查找\n",
    "\n",
    "+ 3、注意的坑：在实例方法中如果要寻找某个变量，会先到实例中的__dict__中寻找。在这里说会到实例中的__dict__中寻找，但是这种变量寻找机制的前提条件是,你的变量名得是self.变量名。不然会报错"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student():\n",
    "    # 此处定义的是类变量\n",
    "    name  = '梁山广'\n",
    "    age = 27\n",
    "    def __init__(self, name, age):\n",
    "        # 此处定义的是实例变量\n",
    "        self.name = name\n",
    "        self.age = age"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'name': '王蕊', 'age': 26}"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "student = Student(\"王蕊\", 26)\n",
    "student.__dict__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-8 实例变量与类变量总结\n",
    "\n",
    "+ 1、类：\n",
    "  + 一、变量（1.类变量 2.实例变量）\n",
    "  + 二、方法：实例方法（以后还有类方法和静态方法）\n",
    "  + 三、构造函数（特殊的实例方法）\n",
    "+ 2、方法与变量的关系：在绝大多数情况下，方法需要对变量做一系列的运算或者一些逻辑上的操作，最终去改变变量的一些状态\n",
    "+ 3、构造函数与实例方法的区别\n",
    "  + 一、调用方式不一样\n",
    "    + 构造函数：类（参数）\n",
    "    + 实例方法：对象.实例方法（参数）\n",
    "  + 二、意义不一样\n",
    "    + 构造函数：初始化类的特征\n",
    "    + 实例方法：描述类的行为\n",
    "  + 4、访问实例变量与类变量\n",
    "    + 实例方法访问实例变量：self.变量\n",
    "    + 实例方法访问类变量：\n",
    "      + 1.self.__class__.变量 \n",
    "      + 2.类.变量\n",
    "    + 在类的外部访问类变量：类.变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "石敢当\n",
      "石敢当\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "class Student():\n",
    "    sum1=0\n",
    "    name=\"xin\"\n",
    "    def __init__(self,name):\n",
    "        self.name=name\n",
    "        print(self.name) #打印的是实例的变量\n",
    "        print(name) #打印的是形参传递的\n",
    "        print(self.__class__.sum1)  #在实例方法中访问类变量\n",
    "    def do_homework(self):\n",
    "        print(\"homework\")\n",
    "\n",
    "student1=Student(\"石敢当\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-10 类方法\n",
    "\n",
    "类方法格式：\n",
    "```python\n",
    "@classmethod\n",
    "def func(cls):\n",
    "    pass\n",
    "```\n",
    "注：这里的cls表示class类，也可以使用别的名字表示，但是建议使用cls这个名字作为参数。此外，类方法中操作类变量，直接使用‘cls.类变量’即可。而且python里对象也可以调用类方法。\n",
    "\n",
    "调用类方法：类名+类方法名：Student.Plus_sum()   或者  对象名+类方法名：student1.Plus_sum()不建议用对象来调用类方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Student():\n",
    "    age = 18\n",
    "    name = \"\"\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        print(self.name + \" is \" + str(self.age) + \" years old!\")\n",
    "        \n",
    "    @classmethod\n",
    "    def modify_sum(cls, age):\n",
    "        cls.age = age\n",
    "        print(cls.name + \" is \" + str(cls.age) + \" years old!\") # 调用类变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "梁山广 is 28 years old!\n",
      " is 28 years old!\n"
     ]
    }
   ],
   "source": [
    "student = Student(\"梁山广\")\n",
    "Student.modify_sum(28) # 直接用类名调用类方法\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "梁山广 is 28 years old!\n"
     ]
    }
   ],
   "source": [
    "student = Student(\"梁山广\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-11 总结\n",
    "\n",
    "+ 实例方法 可以 访问实例变量， `self.实例变量` 或者 `类名.类变量名`\n",
    "+ 实例方法 可以 访问类变量， `类名.类变量名`\n",
    "+ 实例方法 可以 访问静态方法， `self.静态方法名`\n",
    "+ 类方法 可以 访问类变量， `cls.类变量名`\n",
    "+ 类方法 不可以 访问实例变量\n",
    "+ 类方法 可以 访问静态方法，`cls.静态方法名`\n",
    "+ 静态方法 不可以 访问实例变量  # 静态方法 和 类方法区别不太大。\n",
    "+ 静态方法 可以 访问类变量 `类名.类变量`\n",
    "+ 构造函数 是 一种特殊的 实例方法，单独放出来。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-12 成员可见性 public和private\n",
    "\n",
    "+ 成员的可见性\n",
    "为了保证数据的安全，可以让这个数据只在类的内部可以进行操作，而不能在类的外部被操作。\n",
    "\n",
    "如果一个变量或者函数是公开的(public)，那么就可以在类的外部进行访问\n",
    "有时候为了避免被公开访问，可以进行私有化(private)设置,这样在外部就不能进行访问。\n",
    "\n",
    "变成私有的方法：变量或者方法前加双下划线 `__`, 比如 `self.__score = 0`,注意两点\n",
    "+ 构造函数除外，因为后面也有双下划线  `__init__`\n",
    "+ python私有变量很low，只是换了变量名称可以通过`__类名__原来实例变量名`，进行访问原来私有变量,比如`student.__Student__sum`来访问sum"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9-14 继承：\n",
    "\n",
    "```python\n",
    "from  模块  import   Human\n",
    "class  Student(Human):\n",
    "            pass\n",
    "```\n",
    "\n",
    "子类可以继承父类的一切\n",
    "\n",
    "子类调用父类的构造函数：\n",
    "可以直接调用父类的构造函数,但必须传递self参数\n",
    "```python\n",
    "def __init__(self,school,name,age):\n",
    "    self.school = school\n",
    "    Human .__init__(self,name,age)\n",
    "```\n",
    "\n",
    "python中super关键字用法与java不同，它主要是用来在子类中调用父类方法，如在子类构造函数中调用父类构造函数：\n",
    "```python\n",
    "def __init__(self, name, age):\n",
    "    super(子类名, self).__init__(name, age)\n",
    "```\n",
    "也可以调用父类普通方法。\n",
    "\n",
    "当子类的方法和父类的方法同名时，Python会优先调用子类的方法  需要用super关键字去调用父类方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
